﻿using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using EnvDTE80;
using System.Windows.Forms;

namespace FormatX
{
    internal class MainFormat
    {
        #region Identity

        private DTE2 _applicationObject;
        private static List<string> states = new List<string>();
        private SummaryXml sx = null;
        public MainFormat(DTE2 applicationObject)
        {
            _applicationObject = applicationObject;
        }
        #endregion

        #region Method FormatNow
        /// <summary>
        /// 调用后立即开始格式化当前文档
        /// </summary>
        internal void FormatNow()
        {
            try
            {
                Document doc = _applicationObject.ActiveDocument;
                if (doc == null)
                {
                    MessageBox.Show("没有需要格式化的源代码文档", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                Window dow = doc.ActiveWindow;
                FileCodeModel fcm = doc.ProjectItem.FileCodeModel;
                if (fcm == null)
                {
                    MessageBox.Show("不能对单个文件进行格式化，此文件必须存在于项目中。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }
                //  先移除和排序未使用的using并保存
                _applicationObject.ExecuteCommand("Edit.RemoveUnusedUsings", "");
               // _applicationObject.ActiveDocument.Save(doc.FullName);
                //  对文档进行解释，检查是否存在已注释语句块              
                sx = new SummaryXml(doc.Path, doc.FullName, dow.Project.Name, dow.Project.FullName);

                //  将检测到的已注释语句块添加到集合中，在格式化时将跳过这些语句块
                states.AddRange(sx.StartXml());

                CodeElements elts = fcm.CodeElements;
                CodeElement elt = null;
                for (int i = 1; i <= fcm.CodeElements.Count; i++)
                {
                    elt = elts.Item(i);
                    InsertTextEditor(elt, elts, i);
                }
                states.Clear();

                // 执行格式化命令 , 编辑.设置文档的格式（Edit.FormatDocument）
                _applicationObject.ExecuteCommand("Edit.FormatDocument", "");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "格式化出错", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }
        #endregion

        #region Method InsertTextEditor
        /// <summary>
        /// 递归CodeElement对象
        /// </summary>
        /// <param name="elt">CodeElement对象</param>
        /// <param name="elts">一般为CodeElement对象的子集</param>
        /// <param name="loc">一般为CodeElement对象的子集的下标</param>
        public void InsertTextEditor(CodeElement elt, CodeElements elts, long loc)
        {
            switch (elt.Kind)
            {
                //  一般会从NameSpace开始，一直递归到最后一个类型
                case vsCMElement.vsCMElementNamespace:
                    CodeNamespace cns = ((EnvDTE.CodeNamespace)(elt));
                    CodeElements mems_vb = cns.Members;
                    for (int i = 1; i <= cns.Members.Count; i++)
                    {
                        InsertTextEditor(mems_vb.Item(i), mems_vb, i);
                    }
                    break;

                case vsCMElement.vsCMElementClass:
                case vsCMElement.vsCMElementStruct:
                case vsCMElement.vsCMElementInterface:
                    InsertTextEditor(elt);
                    for (int i = 1; i <= elt.Children.Count; i++)
                    {
                        InsertTextEditor(elt.Children.Item(i), elt.Children, i);
                    }
                    break;

                case vsCMElement.vsCMElementEnum:
                    FormatEnum(elt);
                    break;

                case vsCMElement.vsCMElementProperty:
                case vsCMElement.vsCMElementFunction:
                    InsertTextEditor(elt);
                    break;

                #region 如果需要获取类型实现，请使用以下代码
                /*
                 *CodeType 由实现 CodeClass、CodeStruct、CodeInterface、CodeEnum 或 CodeDelegate 的对象实现。 
                 * 您可以确定 CodeElement 是否实现具有 CodeElement.IsCodeType 的 CodeType。 
                 */
                //if (((elt.IsCodeType) & (elt.Kind != vsCMElement.vsCMElementDelegate)))
                //{
                //    CodeType ct = ((EnvDTE.CodeType)(elt));
                //    CodeElements mems = ct.Members;

                //    for (int i = 1; i <= ct.Members.Count; i++)
                //    {
                //        InsertTextEditor(mems.Item(i), mems, i);
                //    }
                //}                
                #endregion
            }
        }
        #endregion

        #region Method InsertTextEditor
        /// <summary>
        /// 非递归方法，添加代码块注释
        /// </summary>
        /// <param name="elt">需要添加代码块注释的对象</param>
        private void InsertTextEditor(CodeElement elt)
        {
            EditPoint epStart = elt.StartPoint.CreateEditPoint();
            EditPoint epEnd = elt.EndPoint.CreateEditPoint();
            string summary = string.Empty;
            string memberName = string.Empty;

            summary = GetMemberName(elt, out memberName);

            if (Check(memberName))
            {
                epStart.StartOfLine();
                epStart.Insert(string.Format("#region {0} {1}\n{2}", SwitchKind(elt.Kind), elt.Name, summary));

                epEnd.EndOfLine();
                epEnd.Insert("\n#endregion");

                states.Add(memberName);
            }
        }
        #endregion

        #region Function GetMemberName
        /// <summary>
        ///  获取函数名（包含参数类型）和注释代码块
        /// </summary>
        /// <param name="elt">COM代码块对象</param>
        /// <param name="memberName">完整函数名（包含参数类型列表）</param>
        /// <returns>经过组合好的注释代码块</returns>
        private string GetMemberName(CodeElement elt, out string memberName)
        {
            StringBuilder sbSummary = new StringBuilder();
            StringBuilder sbParam = new StringBuilder();
            //  添加注释头
            string gs = elt.Kind == vsCMElement.vsCMElementVariable || elt.Kind == vsCMElement.vsCMElementProperty ? "获取或者设置 " : "";
            sbSummary.Append(string.Format(" /// <summary>\n///  {0}{1}\n /// </summary>\n", gs, elt.Name));
            if (elt.Children.Count > 0)
            {
                // 获取参数
                for (int j = 1; j < elt.Children.Count + 1; j++)
                {
                    CodeElement celt = elt.Children.Item(j);
                    if (celt.Kind == vsCMElement.vsCMElementParameter)
                    {
                        sbSummary.Append(string.Format("/// <param name='{0}'>{0}</param>\n", celt.Name));
                        sbParam.Append(((CodeParameter2)celt).Type.AsFullName);
                        if (j < elt.Children.Count)
                        {
                            sbParam.Append(",");
                        }
                    }
                }
            }

            if (sbParam.Length > 0)
            {
                sbParam.Replace('<', '{').Replace('>', '}').Insert(0, "(").Insert(sbParam.Length, ")");
            }

            //  设置组合好的函数名称给外部调用者
            memberName = string.Format("{0}{1}", elt.FullName, sbParam.ToString());
            //  返回组合好的注释代码块
            return sbSummary.ToString();
        }
        #endregion

        #region Method FormatEnum
        /// <summary>
        /// 专属Enum对象调用的方法
        /// </summary>
        /// <param name="elt">vsCMElement对象</param>
        private void FormatEnum(CodeElement elt)
        {
            InsertTextEditor(elt);
            for (int i = 1; i <= elt.Children.Count; i++)
            {
                if (elt.Kind == vsCMElement.vsCMElementEnum)
                {
                    InsertTextEditor(elt.Children.Item(i));
                }
            }
        }
        #endregion

        #region Function SwitchKind
        /// <summary>
        /// 转换vsCMElement对象为友好的代码注释块提示
        /// </summary>
        /// <param name="kind">vsCMElement对象</param>
        /// <returns>友好的代码注释块提示</returns>
        private string SwitchKind(vsCMElement kind)
        {
            string kindtostring = "Code";
            switch (kind)
            {
                case vsCMElement.vsCMElementNamespace:
                    kindtostring = "Namespace";
                    break;
                case vsCMElement.vsCMElementClass:
                    kindtostring = "Class";
                    break;
                case vsCMElement.vsCMElementStruct:
                    kindtostring = "Struct";
                    break;
                case vsCMElement.vsCMElementEnum:
                    kindtostring = "Enum";
                    break;
                case vsCMElement.vsCMElementFunction:
                    kindtostring = "Function";
                    break;
                case vsCMElement.vsCMElementProperty:
                    kindtostring = "Property";
                    break;
                case vsCMElement.vsCMElementVariable:
                    kindtostring = "Variable";
                    break;
            }
            return kindtostring;
        }
        #endregion

        #region Function Check
        /// <summary>
        /// 检查当前传入的对象是否未处理，如果未处理，则为true
        /// </summary>
        /// <param name="name">代码块对象名</param>
        /// <returns>如果未处理，则为true，否则为false，表示已处理</returns>
        private bool Check(string name)
        {
            return states.Contains(name) == false;
        }
        #endregion
    }
}
